package com.eternal.base.data.source;

import com.clj.fastble.data.BleDevice;
import com.eternal.base.R;
import com.eternal.base.concat.DeviceIndex;
import com.eternal.base.concat.DeviceInfo;
import com.eternal.base.concat.DeviceModel;
import com.eternal.base.concat.DeviceModelInfo;
import com.eternal.base.concat.DeviceName;
import com.eternal.base.concat.DeviceSetting;
import com.eternal.base.concat.NotificationName;
import com.eternal.base.concat.PortInfo;
import com.eternal.base.database.BaseDatabase;
import com.eternal.base.database.dao.DeviceDao;
import com.eternal.base.database.dao.NotificationDao;
import com.eternal.base.database.entity.Device;
import com.eternal.base.database.entity.Notification;
import com.eternal.base.exception.BleException;
import com.eternal.base.global.BluetoothKey;
import com.eternal.base.protocol.ProtocolResolution;
import com.eternal.base.protocol.ProtocolTransformer;
import com.eternal.framework.utils.GsonUtils;
import com.eternal.framework.utils.KLog;
import com.eternal.framework.utils.Utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import io.reactivex.Flowable;

public class DeviceSource implements IDeviceSource {
    private static final String DEFAULT_AUTOMATIC_NAME = "Automations";
    private static final String DEFAULT_ALARMS = "Alarms";
    private static final String DEFAULT_NOTIFICATIONS = "Notifications";

    private static final String DEFAULT_NAME = "New Device";
    private DeviceDao dDao;
    private NotificationDao nDao;

    public DeviceSource() {
        dDao = BaseDatabase.getInstance().deviceDao();
        nDao = BaseDatabase.getInstance().notificationDao();
    }

    @Override
    public List<String> getAllMac() {
        return dDao.loadAllMac();
    }

    @Override
    public byte getType(String mac) {
        return dDao.loadDeviceType(mac);
    }

    @Override
    public Flowable<List<DeviceInfo>> getInfo() {
        return dDao.loadAllDeviceInfo();
    }

    @Override
    public List<DeviceInfo> getPortInfo(String mac) {
        return dDao.loadPortInfo(mac);
    }

    @Override
    public void deleteDevice(String mac) {
        dDao.delete(mac);
    }

    @Override
    public void addDevice(Device device) {
        dDao.insert(device);
    }

    @Override
    public void setSettings(String mac, byte port, DeviceSetting setting) {
        if (setting.name == null) {
            dDao.updateSetting(mac, port, setting.brightness, setting.isDegree,
                    setting.transitionTemperature, setting.transitionHumidity,
                    setting.calibrationTemperature, setting.calibrationHumidity);
        } else {
            dDao.updateSetting(mac, port, setting.name, setting.brightness, setting.isDegree,
                    setting.transitionTemperature, setting.transitionHumidity,
                    setting.calibrationTemperature, setting.calibrationHumidity);
        }
    }

    @Override
    public DeviceSetting getSetting(String mac, byte port) {
        return dDao.loadSettings(mac, port);
    }

    @Override
    public void setTime(String mac, long time) {
        dDao.updateTime(mac, time);
    }

    @Override
    public void setControlTypeByHum(String mac, byte port, boolean controlTypeByHum) {
        dDao.updateControlTypeByHum(mac, port, controlTypeByHum);
    }

    @Override
    public boolean isDegree(String mac) {
        return BaseDatabase.getInstance().deviceDao().isDegree(mac);
    }

    @Override
    public DeviceModel getModel(String mac, byte port) {
        return dDao.loadDeviceModel(mac, port);
    }

    @Override
    public Flowable<DeviceName> getDeviceName(String mac, byte port) {
        return dDao.loadDeviceName(mac, port);
    }

    @Override
    public DeviceModelInfo getModelInfo(String mac) {
        return null;
    }

    @Override
    public void setDeviceModel(String mac, byte port, DeviceModel model) {
        dDao.updateDeviceModel(mac, port, model.isDegree, model.tmp, model.hum, model.fan, model.isControlTypeByHum,
                model.workType, model.typeOn, model.typeOff, model.timeOn, model.timeOff, model.cycleOn, model.cycleOff,
                model.schedOn, model.schedOff, model.autoHighHum, model.autoLowHum, model.autoHighTmp, model.autoLowTmp,
                model.autoHighTmpSwitch, model.autoLowTmpSwitch, model.autoHighHumSwitch, model.autoLowHumSwitch,
                model.currentTypeResidueTime, model.currentTypeResidueOn);
    }

    @Override
    public void setModelData(String mac, DeviceModel model) {
        dDao.updateModel(mac, model.timeOn, model.timeOff, model.cycleOn, model.cycleOff, model.schedOn,
                model.schedOff, model.autoHighHum, model.autoLowHum, model.autoHighTmp, model.autoLowTmp,
                model.autoHighTmpSwitch, model.autoLowTmpSwitch, model.autoHighHumSwitch, model.autoLowHumSwitch, model.port, model.choosePort);
    }

    @Override
    public void setDeviceInfo(String mac, DeviceInfo model) {
        dDao.updateDeviceInfo(mac, model.isDegree, model.tmp, model.hum, model.fan, model.tmpState, model.humState, model.choosePort, model.fanType);
    }

    @Override
    public void setPortInfo(String mac, PortInfo model) {
        dDao.updatePortInfo(mac, model.id, model.fanState, model.fan, model.fanType, model.isPlug);
    }

    @Override
    public void initNotification(String mac, byte port, List<Notification> data) {
        List<NotificationName> list = nDao.loadMacAllName(mac, port);
        List<Integer> auto = new ArrayList<>(), notify = new ArrayList<>(), alarms = new ArrayList<>();
        for (NotificationName name : list) {
            if (name.type == BluetoothKey.ALARMS) {
                getNumberZero(alarms, DEFAULT_ALARMS, name.name);
            } else if (name.type == BluetoothKey.AUTOMATION) {
                getNumberZero(auto, DEFAULT_AUTOMATIC_NAME, name.name);
            } else {
                getNumberZero(notify, DEFAULT_NOTIFICATIONS, name.name);
            }
        }
        boolean has;
        Iterator<NotificationName> iter;
        NotificationName name;
        for (Notification notification : data) {
            notification.mac = mac;
            notification.port = port;
            has = false;
            iter = list.iterator();
            while (iter.hasNext()) {
                name = iter.next();
                if (name.id == notification.id && name.type == notification.type) {
                    has = true;
                    iter.remove();
                    notification.name = name.name;
                    break;
                }
            }
            if (has) {
                nDao.update(notification);
            } else {
                int num;
                if (notification.type == BluetoothKey.AUTOMATION) {
                    num = minNumberZero(auto);
                    notification.name = (num == 0 ? DEFAULT_AUTOMATIC_NAME : DEFAULT_AUTOMATIC_NAME + " " + num);
                    auto.add(num);
                } else if (notification.type == BluetoothKey.ALARMS) {
                    num = minNumberZero(alarms);
                    notification.name = (num == 0 ? DEFAULT_ALARMS : DEFAULT_ALARMS + " " + num);
                    alarms.add(num);
                } else {
                    num = minNumberZero(notify);
                    notification.name = (num == 0 ? DEFAULT_NOTIFICATIONS : DEFAULT_NOTIFICATIONS + " " + num);
                    notify.add(num);
                }
                nDao.insertNotification(notification);
            }
        }
        for (NotificationName temp : list) {
            nDao.deleteNotification(mac, port, temp.id, temp.type);
        }
    }

    @Override
    public DeviceIndex connect(Device device) {
        DeviceIndex result = null;
        List<DeviceName> names = dDao.loadAllName();
        List<Integer> list = new ArrayList<>(names.size());
        DeviceName temp;
        for (int i = 0; i < names.size(); ++i) {
            temp = names.get(i);
            if (temp.mac.equals(device.mac)) {
                result = new DeviceIndex(i, temp.name);
                break;
            } else if (temp.name.startsWith(DEFAULT_NAME)) {
                if (temp.name.length() == 10) {
                    list.add(1);
                } else {
                    try {
                        list.add(Integer.valueOf(temp.name.substring(11)));
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        if (result == null) {
            int num = minNumber(list);
            device.name = num == 1 ? DEFAULT_NAME : DEFAULT_NAME + " " + num;
            dDao.insert(device);
            KLog.i("save device:" + device.mac);
            result = new DeviceIndex(-1, device.name);
        }
        result.typeName = Utils.getString(R.string.tip_name_name,
                ProtocolTransformer.getType(device.type),
                device.typeName);
        return result;
    }

    /**
     * 获取默认最小下标
     *
     * @param list
     * @return
     */
    private int minNumber(List<Integer> list) {
        if (list.isEmpty()) {
            return 1;
        } else if (list.size() == 1) {
            if (list.get(0) == 1) {
                return 2;
            } else {
                return 1;
            }
        }
        int min = 0, i, j;
        for (i = 1; i < list.size(); ++i) {
            if (list.get(min) > list.get(i)) {
                min = i;
            }
        }
        if (list.get(min) != 1) {
            return 1;
        }
        Collections.swap(list, min, 0);
        for (i = 1; i < list.size(); ++i) {
            min = i;
            for (j = i + 1; j < list.size(); ++j) {
                if (list.get(min) > list.get(j)) {
                    min = j;
                }
            }
            Collections.swap(list, i, min);
            if (list.get(i) - list.get(i - 1) > 1) {
                return list.get(i - 1) + 1;
            }
        }
        return list.size() + 1;
    }

    private int minNumberZero(List<Integer> list) {
        if (list.isEmpty()) {
            return 0;
        } else if (list.size() == 1) {
            if (list.get(0) == 0) {
                return 1;
            } else {
                return 0;
            }
        }
        int min = 0, i, j;
        for (i = 1; i < list.size(); ++i) {
            if (list.get(min) > list.get(i)) {
                min = i;
            }
        }
        if (list.get(min) != 0) {
            return 0;
        }
        Collections.swap(list, min, 0);
        for (i = 1; i < list.size(); ++i) {
            min = i;
            for (j = i + 1; j < list.size(); ++j) {
                if (list.get(min) > list.get(j)) {
                    min = j;
                }
            }
            Collections.swap(list, i, min);
            if (list.get(i) - list.get(i - 1) > 1) {
                return list.get(i - 1) + 1;
            }
        }
        return list.size();
    }

    private void getNumberZero(List<Integer> numbers, String prefix, String name) {
        if (name.startsWith(prefix)) {
            if (name.length() == prefix.length()) {
                numbers.add(0);
            } else {
                try {
                    numbers.add(Integer.valueOf(name.substring(prefix.length() + 1)));
                } catch (NumberFormatException e) {
                }
            }
        }
    }
}
